#include "scene.hpp"
#include <iostream>

SceneNode::SceneNode(const std::string& name)
  : m_name(name)
{
}

SceneNode::~SceneNode()
{
}

void SceneNode::rotate(char axis, double angle)
{
  // Fill me in
	angle = angle/180*M_PI;
    Vector4D row1, row2, row3, row4;
	switch( axis ){
		case 'x':
			row1 = Vector4D(1.0, 0.0, 0.0, 0.0);
			row2 = Vector4D(0.0, cos(angle), -sin(angle), 0.0);
			row3 = Vector4D(0.0, sin(angle), cos(angle), 0.0);
			row4 = Vector4D(0.0, 0.0, 0.0, 1.0);
			break;
		case 'y':
			row1 = Vector4D(cos(angle), 0.0, sin(angle), 0.0);
			row2 = Vector4D(0.0, 1.0, 0.0, 0.0);
			row3 = Vector4D(-sin(angle), 0.0, cos(angle), 0.0);
			row4 = Vector4D(0.0, 0.0, 0.0, 1.0);
			break;
		case 'z':
			row1 = Vector4D(cos(angle), -sin(angle), 0.0, 0.0);
			row2 = Vector4D(sin(angle), cos(angle), 0.0, 0.0);
			row3 = Vector4D(0.0, 0.0, 1.0, 0.0);
			row4 = Vector4D(0.0, 0.0, 0.0, 1.0);
			break;
	}
	Matrix4x4 rot = Matrix4x4(row1, row2, row3, row4);
    set_transform( get_transform() * rot );
}

void SceneNode::scale(const Vector3D& amount)
{
  // Fill me in
	Vector4D row1( amount[0], 0.0, 0.0, 0.0);
	Vector4D row2( 0.0, amount[1], 0.0, 0.0);
	Vector4D row3( 0.0, 0.0, amount[2], 0.0);
	Vector4D row4( 0.0, 0.0, 0.0, 1.0);
	
	Matrix4x4 scale = Matrix4x4(row1, row2,row3, row4);
    set_transform( get_transform() * scale );
}

void SceneNode::translate(const Vector3D& amount)
{
  // Fill me in
	Vector4D row1( 1.0, 0.0, 0.0, amount[0]);
	Vector4D row2( 0.0, 1.0, 0.0, amount[1]);
	Vector4D row3( 0.0, 0.0, 1.0, amount[2]);
	Vector4D row4( 0.0, 0.0, 0.0, 1.0);
	
	Matrix4x4 translate = Matrix4x4(row1, row2,row3, row4);
    set_transform( get_transform() * translate );
}

bool SceneNode::is_joint() const
{
  return false;
}

bool SceneNode::ray_intersect(Ray ray, Intersection *intersection)
{
	double MAX_DIST = 1000000.0f;
	bool hit = false;
	ray.set_position(m_invtrans * ray.get_position());
	ray.set_direction(m_invtrans * ray.get_direction());
	for (ChildList::const_iterator node = m_children.begin(); node != m_children.end(); node++) {
		bool temp_hit = (*node)->ray_intersect(ray, intersection);
		if( temp_hit && intersection->m_dist < MAX_DIST){
			MAX_DIST = intersection->m_dist;
		}
		if( temp_hit ) hit = true;
    }
	if(hit){
		intersection->m_normal = (m_invtrans.transpose()) * intersection->m_normal;
		intersection->m_intersect = m_trans * intersection->m_intersect;
	}
	return hit;
}

JointNode::JointNode(const std::string& name)
  : SceneNode(name)
{
}

JointNode::~JointNode()
{
}

bool JointNode::is_joint() const
{
  return true;
}
 
void JointNode::set_joint_x(double min, double init, double max)
{
  m_joint_x.min = min;
  m_joint_x.init = init;
  m_joint_x.max = max;
}

void JointNode::set_joint_y(double min, double init, double max)
{
  m_joint_y.min = min;
  m_joint_y.init = init;
  m_joint_y.max = max;
}

GeometryNode::GeometryNode(const std::string& name, Primitive* primitive)
  : SceneNode(name),
    m_primitive(primitive)
{
}

GeometryNode::~GeometryNode()
{
}

bool GeometryNode::ray_intersect(Ray ray, Intersection *intersection)
{
	ray.set_position(m_invtrans * ray.get_position());
	ray.set_direction(m_invtrans * ray.get_direction());
	bool hit = false;
	if(m_primitive){
		hit = m_primitive->RayIntersect(ray, intersection);
		if(hit){
			intersection->m_material = m_material;
			intersection->m_hit_node = this;
			intersection->m_normal = (m_invtrans.transpose()) * intersection->m_normal;
			intersection->m_intersect = m_trans * intersection->m_intersect;
		}
	}
	return hit;
}
